package edu.gatech.bsbmule.model;

import java.awt.Point;
import java.util.Random;

import com.google.gson.Gson;

/**
 * This class represents a map in the game. It consists of various types of
 * tiles
 * 
 * @author Hanjie, Greg
 * @version 1.0 10/6/2013
 * 
 */
public class Map {
	private Tile[][] tiles;

	private int mountain1; // number of mountain type 1
	private int mountain2; // number of mountain type 2
	private int mountain3; // number of mountain type 3

	public static final int MAP_STANDARD = 0;
	public static final int MAP_RANDOM = 1;
	
	private static Map instance = null;
	private static final Gson gson = new Gson();
	
	
	/**
	 * 
	 * @return the instance of the map so it can be accessed publicly
	 */
	public static Map getInstance() {
		if (instance == null) {
			instance = new Map();
		}
		return instance;
	}
	
	/**
	 * Creates the grid and adds the town to the map
	 * and sets the number of mountains
	 */
	private Map() {
		tiles = new Tile[5][9];
		tiles[2][4] = new Tile(1, new Point(2,4));

		mountain1 = 3;
		mountain2 = 3;
		mountain3 = 2;
	}
	
	/**
	 * Decides what kind of map to generate based on mapType
	 * @param mapType
	 */
	public void generateMap(int mapType) {
		if (mapType == MAP_STANDARD)
			stanMap();
		else
			// type == MAP_RANDOM
			randMap();
	}
	/**
	 * Creates a vertical river in a random column
	 * and assigns random numbers for the mountains
	 * then generates the mountains and plains
	 */
	private void randMap() {
		Random rand = new Random();
		genRiver(rand.nextInt(8));
		mountain1 = rand.nextInt(5) + 1; // between 1 and 6 inclusive
		mountain2 = rand.nextInt(4) + 1;
		mountain3 = rand.nextInt(2) + 2;
		genMountains();
		genPlains();
	}
	
	/**
	 * generates the river vertically in the center column in the map
	 * then generates the mountains and plains
	 */
	private void stanMap() {
		genRiver(4);
		genMountains();
		genPlains();
	}
	
	/**
	 * Places river tiles in each row at column i as long as
	 * the town isn't there
	 */
	private void genRiver(int i){
		int j = 0;
		for (Tile[]  a: tiles)
		{
			for (int k = 0; k < a.length; k++){
				if (k == i)
					// if (tiles[j][k].getTerrain() != 1) //checks if town is
					// there -- it wasn't here...
					if (tiles[j][k] == null)
						tiles[j][k] = new Tile(3, new Point(j,k));
			}
			j++;
		}
	}
	/**
	 * Places all the mountains in the game randomly
	 */
	private void genMountains() {
		Random rand = new Random();
		int count = 4; // count is mountain type that game is creating
		for (int j = 0; j < mountain1 + mountain2 + mountain3; j++) {
			int y = rand.nextInt(8);
			int x = rand.nextInt(4);
			addMountain(x, y, count);
			if (j == mountain1)
				count++;
			if (j == mountain1 + mountain2)
				count++;
		}
	}

	/**
	 * 
	 * @param x the horizontal coordinate of where the mountain is to be placed
	 * @param y the vertical coordinate of where the mountain is to be placed
	 * @param type the type of mountain to be placed
	 * 
	 *  Method tries to add a mountain at position (x, y) but if there is something in the way
	 *  it tries to place a tile in one space to the right.
	 */
	private void addMountain(int x, int y, int type) {
		//System.out.println(""+x+", "+y); //debugging
		if (x > 4)
			addMountain(0, y+1, type);
		else if (y > 8)
			addMountain(x+1, 0, type);
		else if (x > 4 && y > 8)
			addMountain(0, 0, type);
		else if (tiles[x][y] != null){ // checks if tile is occupied
			addMountain(x, y+1, type);
		}
		else
			tiles[x][y] = new Tile(type, new Point(x,y));
	}
	
	/**
	 * Fills in all blank spaces with plains tiles
	 */
	private void genPlains() {
		for (int j = 0; j < tiles.length; j++) {
			for (int k = 0; k < tiles[j].length; k++) {
				if (tiles[j][k] == null) // checks if tile is occupied
					tiles[j][k] = new Tile(2, new Point(j,k));
			}
		}
	}
	
	/**
	 * 
	 * @return a 2D array of all the tiles
	 */
	public Tile[][] getTiles() {
		return tiles;
	}

	/**
	 * 
	 * @param x horizontal coordinate
	 * @param y vertiacal coordinate
	 * @return the Tile at position (x, y)
	 */
	public Tile getTile(int x, int y) {
		return tiles[x][y];
	}
	
	/**
	 * Saves the game state to a text file
	 * @return
	 */
	public static String saveToJson() {
		return gson.toJson(instance);
	}
	
	/**
	 * Loads a game state from the text file
	 * @param json
	 */
	public static void loadFromJson(String json) {
		instance = gson.fromJson(json, Map.class);
	}
}
